package actor

import (
	"reflect"
	"sync"

	sadefine "gitee.com/simplexyz/simplego/actor/define"
	salog "gitee.com/simplexyz/simplego/actor/log"
	satimer "gitee.com/simplexyz/simplego/actor/timer"
	sawork "gitee.com/simplexyz/simplego/actor/work"
	sdebug "gitee.com/simplexyz/simplego/debug"
)

type Actor struct {
	*option

	pid *sadefine.PID

	startedOnce  sync.Once
	stoppingOnce sync.Once
	stoppedOnce  sync.Once

	timerMgr *satimer.Manager
}

func create(option *option) *Actor {
	a := &Actor{
		option: option,
	}

	if a.startedWg == nil {
		a.startedWg = &sync.WaitGroup{}
	}

	a.startedWg.Add(1)

	if a.decider == nil {
		a.decider = StopDecider
	}

	if a.mailBoxSize < 0 {
		a.mailBoxSize = 0
	}

	if a.logger == nil {
		a.logger = salog.Get()
	}

	return a
}

func executeContextFuncs(funcs []func(sadefine.Context), ctx sadefine.Context) {
	for _, f := range funcs {
		if f == nil {
			continue
		}
		f(ctx)
	}
}

func (a *Actor) Receive(ctx sadefine.Context) {
	switch msg := ctx.Message().(type) {
	case *sawork.MessagePostWork:
		msg.Work.Execute(ctx)
		sawork.DestroyMessagePostWork(msg)

	case *sawork.MessageDispatchWork:
		msg.Err = msg.Work.Execute(ctx)
		ctx.Respond(msg)

	case *satimer.MessageTimeout:
		defer satimer.DestroyMessageTimeout(msg)

		for _, f := range a.beforeTriggerTimerFuncs {
			if f == nil {
				continue
			}
			ok, err := f(msg.ID, msg.Tag, ctx)
			if !ok {
				return
			}
			if err != nil {
				a.logger.Errorf("[%s] execute BeforeTriggerTimerFunc fail, id=%d, tag=%d, %s", a.pid.Id, msg.ID, msg.Tag, err)
				return
			}
		}

		err := a.timerMgr.Trigger(ctx, msg.ID)
		if err != nil {
			a.logger.Errorf("[%s] trigger timer fail, id=%d, tag=%d, %s", a.pid.Id, msg.ID, msg.Tag, err)
			return
		}

		for _, f := range a.afterTriggerTimerFuncs {
			if f == nil {
				continue
			}
			f(err, msg.ID, msg.Tag, ctx)
		}

	case *sadefine.Started:
		a.startedOnce.Do(func() {
			defer func() {
				if p := recover(); p != nil {
					sdebug.WriteStackTraceFile(p, "")
					ctx.Stop(ctx.Self())
				}

				if a.stoppedWg != nil {
					a.stoppedWg.Add(1)
				}

				a.startedWg.Done()
			}()

			a.pid = ctx.Self().Clone()

			if a.name == "" {
				a.name = a.pid.Id
			}

			a.timerMgr = satimer.NewManager(RootContext(), a.pid.Clone())

			executeContextFuncs(a.onStartedFuncs, ctx)
		})

	case *sadefine.Stopping:
		a.stoppingOnce.Do(func() {
			defer func() {
				if p := recover(); p != nil {
					sdebug.WriteStackTraceFile(p, "")
				}
			}()

			executeContextFuncs(a.onStoppingFuncs, ctx)
		})

	case *sadefine.Stopped:
		a.stoppedOnce.Do(func() {
			defer func() {
				if p := recover(); p != nil {
					sdebug.WriteStackTraceFile(p, "")
				}

				if a.timerMgr != nil {
					a.timerMgr.StopAll()
					a.timerMgr = nil
				}

				if a.stoppedWg != nil {
					a.stoppedWg.Done()
				}

				a.option.destroy()
				a.option = nil
			}()

			executeContextFuncs(a.onStoppedFuncs, ctx)
		})

	case *sadefine.Restarting:
		//defer func() {
		//	if p := recover(); p != nil {
		//		sdebug.WriteStackTraceFile(p, "")
		//	}
		//}()

		executeContextFuncs(a.onRestartingFuncs, ctx)

	case *sadefine.Terminated:
		for _, f := range a.onTerminatedFuncs {
			if f == nil {
				continue
			}
			f(ctx, msg.Who, msg.Why)
		}

	default:
		if funcs, ok := a.onReceiveMessageFuncs[reflect.TypeOf(msg)]; ok {
			for _, f := range funcs {
				if f == nil {
					continue
				}
				if !f(ctx) {
					break
				}
			}
		}
	}
}
